﻿#include "debug_server_impl.hh"

#include "../../thirdparty/jsoncpp/include/json/json.h"
#include "../box/box_channel.hh"
#include "../detail/script_debug_server.hh"
#include "../util/lua/lua_helper.hh"
#include "../util/os_util.hh"
#include "../util/string_util.hh"
#include "../util/websocket/websocket_impl.hh"
#include "script_debugger.hh"
#include <fstream>

#include <filesystem>

using namespace kratos::ws;
namespace fs = std::filesystem;

kratos::service::DebugServerImpl::DebugServerImpl(
    kratos::service::ServiceBox *box) {
  box_ = box;
}

kratos::service::DebugServerImpl::~DebugServerImpl() {}

auto kratos::service::DebugServerImpl::start(const std::string &ip, int port)
    -> bool {
  register_all_command();
  websocket_server_ptr_ =
      kratos::make_shared_pool_ptr<WebSocketServerImpl>(box_);
  if (!websocket_server_ptr_->startup(
          std::bind(&DebugServerImpl::server_callback, this,
                    std::placeholders::_1, std::placeholders::_2))) {
    return false;
  }
  auto network_ptr = std::dynamic_pointer_cast<kratos::service::BoxNetwork>(
      websocket_server_ptr_);
  if (!network_ptr) {
    return false;
  }
  return network_ptr->listen_at("ScriptDebugClient", ip, port);
}

auto kratos::service::DebugServerImpl::stop() -> bool {
  debugger_map_.clear();
  return websocket_server_ptr_->shutdown();
}

auto kratos::service::DebugServerImpl::update() -> void {
  std::dynamic_pointer_cast<kratos::service::BoxNetwork>(websocket_server_ptr_)
      ->update();
}

auto kratos::service::DebugServerImpl::enable_machine(
    const std::string &name, ScriptService *service,
    kratos::service::ScriptDebugger *debugger) -> void {
  debugger->enable();
  debugger->set_cb(std::bind(&DebugServerImpl::debugger_callback, this,
                             std::placeholders::_1));
  debugger_map_[name] = {debugger, service};
}

auto kratos::service::DebugServerImpl::disable_machine(const std::string &name)
    -> void {
  auto it = debugger_map_.find(name);
  if (it == debugger_map_.end()) {
    return;
  }
  it->second.debugger->disable();
  it->second.debugger->set_cb(nullptr);
  debugger_map_.erase(it);
}

auto kratos::service::DebugServerImpl::register_all_command() -> void {
  DEGUB_COMMAND_REGISTRY(attach);
  DEGUB_COMMAND_REGISTRY(detach);
  DEGUB_COMMAND_REGISTRY(get_machine_list);
  DEGUB_COMMAND_REGISTRY(get_tree);
  DEGUB_COMMAND_REGISTRY(step_in);
  DEGUB_COMMAND_REGISTRY(step_out);
  DEGUB_COMMAND_REGISTRY(step_over);
  DEGUB_COMMAND_REGISTRY(continue);
  DEGUB_COMMAND_REGISTRY(get_breakpoint);
  DEGUB_COMMAND_REGISTRY(add_breakpoint);
  DEGUB_COMMAND_REGISTRY(remove_breakpoint);
  DEGUB_COMMAND_REGISTRY(enable_breakpoint);
  DEGUB_COMMAND_REGISTRY(disable_breakpoint);
  DEGUB_COMMAND_REGISTRY(enable_debugger);
  DEGUB_COMMAND_REGISTRY(disable_debugger);
  DEGUB_COMMAND_REGISTRY(upvalues);
  DEGUB_COMMAND_REGISTRY(locals);
  DEGUB_COMMAND_REGISTRY(list);
  DEGUB_COMMAND_REGISTRY(print);
  DEGUB_COMMAND_REGISTRY(eval);
  DEGUB_COMMAND_REGISTRY(open_file);
  DEGUB_COMMAND_REGISTRY(save_file);
  DEGUB_COMMAND_REGISTRY(add_file);
  DEGUB_COMMAND_REGISTRY(delete_file);
  DEGUB_COMMAND_REGISTRY(add_dir);
  DEGUB_COMMAND_REGISTRY(remove_dir);
  DEGUB_COMMAND_REGISTRY(rename_dir);
  DEGUB_COMMAND_REGISTRY(reload);
  DEGUB_COMMAND_REGISTRY(get_thread);
  DEGUB_COMMAND_REGISTRY(backtrace);
  DEGUB_COMMAND_REGISTRY(frame);
  DEGUB_COMMAND_REGISTRY(restart);
  DEGUB_COMMAND_REGISTRY(get_log);
}

auto kratos::service::DebugServerImpl::register_command(
    const std::string &command, ClassMethod method) -> void {
  register_command(command,
                   std::bind(method, this, std::placeholders::_1,
                             std::placeholders::_2, std::placeholders::_3));
}

auto kratos::service::DebugServerImpl::register_command(
    const std::string &command, CommandCallback cb) -> void {
  command_map_[command] = cb;
}

auto kratos::service::DebugServerImpl::server_callback(
    WebSocketEvent e, WebSocketChannelPtr channel) -> void {
  switch (e) {
  case WebSocketEvent::ACCEPT:
    break;
  case WebSocketEvent::RECV:
    on_client_command(channel);
    break;
  case WebSocketEvent::CLOSE:
    channel_post_close(channel);
    break;
  default:
    break;
  }
}

auto kratos::service::DebugServerImpl::on_client_command(
    WebSocketChannelPtr channel) -> void {
  std::string command;
  channel->recv(command);
  Json::Value root;
  std::string error;
  Json::Value result;
  if (!kratos::util::get_json_root(command, root, error)) {
    result["error"] = "Bad protocol";
    channel->send(result.toStyledString());
    return;
  }
  auto it = command_map_.find(root["command"].asString());
  if (it == command_map_.end()) {
    return;
  }
  it->second(root, result, channel);
  channel->send(result.toStyledString());
}

void get_dir_children(Json::Value &root, const std::string &path) {
  fs::path folder(path);
  if (!fs::exists(folder) || !fs::is_directory(folder)) {
    return;
  }
  fs::directory_iterator end;
  for (fs::directory_iterator it(folder); it != end; it++) {
    auto dir_name = (*it).path().string();
    auto file_name = (*it).path().filename().string();
    if (fs::is_directory(*it)) {
      Json::Value parent;
      parent["name"] = file_name;
      get_dir_children(parent["children"], dir_name);
      root.append(parent);
    } else {
      if (kratos::util::endWith(file_name, ".lua")) {
        root.append(file_name);
      }
    }
  }
}

auto kratos::service::DebugServerImpl::get_debugger(const std::string &command,
                                                    const Json::Value &root,
                                                    Json::Value &result,
                                                    WebSocketChannelPtr channel)
    -> ScriptDebugger * {
  result["command"] = command;
  if (!root.isMember("machine_name")) {
    result["error"] = "Invalid protocol";
    return nullptr;
  }
  auto machine_name = root["machine_name"].asString();
  if (command != "attach") {
    auto machine_channel_it = debugger_channel_.find(machine_name);
    if (machine_channel_it == debugger_channel_.end()) {
      result["error"] = "Debugger need attach";
      return nullptr;
    }
    if (command != "detach") {
      if (machine_channel_it->second != channel->get_id()) {
        result["error"] = "Debugger need attach";
        return nullptr;
      }
    }
  }
  auto it = debugger_map_.find(machine_name);
  if (it == debugger_map_.end()) {
    result["error"] = "Debugger not found";
    return nullptr;
  }
  result["machine_name"] = machine_name;
  result["error"] = "ok";
  result["unique_id"] = it->second.debugger->get_unique_id();
  return it->second.debugger;
}

auto kratos::service::DebugServerImpl::channel_post_close(
    WebSocketChannelPtr channel) -> void {
  for (const auto &[name, id] : debugger_channel_) {
    if (id == channel->get_id()) {
      auto it = debugger_map_.find(name);
      if (it != debugger_map_.end()) {
        it->second.debugger->disable();
      }
      debugger_channel_.erase(name);
      return;
    }
  }
}

auto kratos::service::DebugServerImpl::debugger_callback(
    const ScriptDebugger &debugger) -> void {
  Json::Value result;
  result["error"] = "ok";
  result["command"] = "trigger_breakpoint";
  result["machine_name"] = debugger.get_name();
  result["stack"] = debugger.get_stack().to_string();
  result["unique_id"] = debugger.get_unique_id();
  auto machine_channel_it = debugger_channel_.find(debugger.get_name());
  if (machine_channel_it == debugger_channel_.end()) {
    // TODO error
    return;
  }
  auto channel_id = machine_channel_it->second;
  auto ws_channel = websocket_server_ptr_->get_channel(channel_id);
  if (!ws_channel) {
    // TODO error
    return;
  }
  ws_channel->send(result.toStyledString());
}

DEBUG_COMMAND_HANDLE_IMPL(attach) {
  auto *debugger = get_debugger("attach", root, result, nullptr);
  if (!debugger) {
    return;
  }
  auto machine_name = root["machine_name"].asString();
  auto it = debugger_channel_.find(machine_name);
  if (it != debugger_channel_.end()) {
    if (it->second != channel->get_id()) {
      result["error"] = "Debugger only allows one client";
    }
    return;
  }
  debugger_channel_[machine_name] = channel->get_id();
  debugger->set_name(machine_name);
  debugger->enable();
  result["machine_version"] = debugger->get_machine_version();
}

DEBUG_COMMAND_HANDLE_IMPL(detach) {
  auto *debugger = get_debugger("detach", root, result, nullptr);
  if (!debugger) {
    return;
  }
  auto machine_name = root["machine_name"].asString();
  debugger_channel_.erase(machine_name);
  debugger->set_name("");
  debugger->disable();
}

/**
 * @brief 获取虚拟机列表
 */
DEBUG_COMMAND_HANDLE_IMPL(get_machine_list) {
  result["command"] = "get_machine_list";
  for (const auto &[name, module] : debugger_map_) {
    Json::Value value;
    value["name"] = name;
    value["enable"] = module.debugger->is_enable();
    result["result"].append(value);
  }
}

DEBUG_COMMAND_HANDLE_IMPL(get_tree) {
  auto *debugger = get_debugger("get_tree", root, result, channel);
  if (!debugger) {
    return;
  }
  if (debugger->get_source_root().empty()) {
    result["error"] = "Source path not set";
  } else {
    get_dir_children(result["tree"], debugger->get_source_root());
  }
}

DEBUG_COMMAND_HANDLE_IMPL(step_in) {
  auto *debugger = get_debugger("step_in", root, result, channel);
  if (!debugger) {
    return;
  }
  std::string error;
  if (!debugger->step_in(error)) {
    result["error"] = error;
  }
}

DEBUG_COMMAND_HANDLE_IMPL(step_out) {
  auto *debugger = get_debugger("step_out", root, result, channel);
  if (!debugger) {
    return;
  }
  std::string error;
  if (!debugger->step_out(error)) {
    result["error"] = error;
  }
}
DEBUG_COMMAND_HANDLE_IMPL(step_over) {
  auto *debugger = get_debugger("step_over", root, result, channel);
  if (!debugger) {
    return;
  }
  std::string error;
  if (!debugger->step_over(error)) {
    result["error"] = error;
  }
}
DEBUG_COMMAND_HANDLE_IMPL(continue) {
  auto *debugger = get_debugger("continue", root, result, channel);
  if (!debugger) {
    return;
  }
  std::string error;
  if (!debugger->execute(error)) {
    result["error"] = error;
  }
}
DEBUG_COMMAND_HANDLE_IMPL(get_breakpoint) {
  auto *debugger = get_debugger("get_breakpoint", root, result, channel);
  if (!debugger) {
    return;
  }
  const auto &breakpoints = debugger->get_all_breakpoint();
  for (const auto &[k, v] : breakpoints) {
    Json::Value bp;
    bp["file"] = v.file;
    bp["line"] = v.line;
    bp["break_id"] = k;
    bp["state"] =
        ((v.state == BreakPointState::DISABLE) ? "disable" : "enable");
    result["bp"].append(bp);
  }
}
DEBUG_COMMAND_HANDLE_IMPL(add_breakpoint) {
  auto *debugger = get_debugger("add_breakpoint", root, result, channel);
  if (!debugger) {
    return;
  }
  auto file = root["file"].asString();
  auto line = root["line"].asInt();
  result["break_id"] = debugger->add_breakpoint(file, line);
  result["file"] = file;
  result["line"] = line;
  const auto &breakpoints = debugger->get_all_breakpoint();
  for (const auto &[k, v] : breakpoints) {
    Json::Value bp;
    bp["file"] = v.file;
    bp["line"] = v.line;
    bp["break_id"] = k;
    bp["state"] =
        ((v.state == BreakPointState::DISABLE) ? "disable" : "enable");
    result["bp"].append(bp);
  }
}
DEBUG_COMMAND_HANDLE_IMPL(remove_breakpoint) {
  auto *debugger = get_debugger("remove_breakpoint", root, result, channel);
  if (!debugger) {
    return;
  }
  if (!root.isMember("break_id")) {
    result["error"] = "Invalid protocol";
    return;
  }
  auto break_id = root["break_id"].asString();
  result["break_index"] = root["break_index"];
  debugger->remove_breakpoint(break_id);
  const auto &breakpoints = debugger->get_all_breakpoint();
  for (const auto &[k, v] : breakpoints) {
    Json::Value bp;
    bp["file"] = v.file;
    bp["line"] = v.line;
    bp["break_id"] = k;
    bp["state"] =
        ((v.state == BreakPointState::DISABLE) ? "disable" : "enable");
    result["bp"].append(bp);
  }
}
DEBUG_COMMAND_HANDLE_IMPL(enable_breakpoint) {
  auto *debugger = get_debugger("enable_breakpoint", root, result, channel);
  if (!debugger) {
    return;
  }
  if (!root.isMember("break_id")) {
    result["error"] = "Invalid protocol";
    return;
  }
  auto break_id = root["break_id"].asString();
  auto break_index = root["break_index"].asInt();
  if (break_index == -1) {
    // enable all breakpoint
    int index = 0;
    for (const auto &[k, v] : debugger->get_all_breakpoint()) {
      debugger->enable_breakpoint(k);
      Json::Value bp;
      bp["break_id"] = k;
      bp["break_index"] = index++;
      result["breakpoints"].append(bp);
    }
  } else {
    Json::Value bp;
    debugger->enable_breakpoint(break_id);
    bp["break_id"] = root["break_id"];
    bp["break_index"] = root["break_index"];
    result["breakpoints"].append(bp);
  }
}
DEBUG_COMMAND_HANDLE_IMPL(disable_breakpoint) {
  auto *debugger = get_debugger("disable_breakpoint", root, result, channel);
  if (!debugger) {
    return;
  }
  if (!root.isMember("break_id")) {
    result["error"] = "Invalid protocol";
    return;
  }
  auto break_id = root["break_id"].asString();
  auto break_index = root["break_index"].asInt();
  if (break_index == -1) {
    // disable all breakpoint
    int index = 0;
    for (const auto &[k, v] : debugger->get_all_breakpoint()) {
      debugger->disable_breakpoint(k);
      Json::Value bp;
      bp["break_id"] = k;
      bp["break_index"] = index++;
      result["breakpoints"].append(bp);
    }
  } else {
    Json::Value bp;
    debugger->disable_breakpoint(break_id);
    bp["break_id"] = root["break_id"];
    bp["break_index"] = root["break_index"];
    result["breakpoints"].append(bp);
  }
}
DEBUG_COMMAND_HANDLE_IMPL(enable_debugger) {
  auto *debugger = get_debugger("enable_debugger", root, result, channel);
  if (!debugger) {
    return;
  }
  debugger->enable();
}
DEBUG_COMMAND_HANDLE_IMPL(disable_debugger) {
  auto *debugger = get_debugger("disable_debugger", root, result, channel);
  if (!debugger) {
    return;
  }
  debugger->disable();
}
DEBUG_COMMAND_HANDLE_IMPL(upvalues) {
  auto *debugger = get_debugger("upvalues", root, result, channel);
  if (!debugger) {
    return;
  }
  if (debugger->get_stack().empty()) {
    return;
  }
  result["content"] = debugger->get_stack().upvalues();
}
DEBUG_COMMAND_HANDLE_IMPL(locals) {
  auto *debugger = get_debugger("locals", root, result, channel);
  if (!debugger) {
    return;
  }
  if (debugger->get_stack().empty()) {
    return;
  }
  result["content"] = debugger->get_stack().locals();
}
DEBUG_COMMAND_HANDLE_IMPL(list) {
  auto *debugger = get_debugger("list", root, result, channel);
  if (!debugger) {
    return;
  }
  if (!debugger->is_suspend()) {
    result["error"] = "No thread in debug mode";
    return;
  }
  std::string content;
  const auto &stack = debugger->get_stack();
  if (stack.empty()) {
    return;
  }
  util::get_file_content(stack.file_path(), stack.bp_line(), 5, content);
  result["content"] = content;
}
DEBUG_COMMAND_HANDLE_IMPL(print) {
  auto *debugger = get_debugger("print", root, result, channel);
  if (!debugger) {
    return;
  }
  if (debugger->get_stack().empty()) {
    return;
  }
  std::string value;
  debugger->print(root["name"].asString(), value);
  result["content"] = value;
}
DEBUG_COMMAND_HANDLE_IMPL(eval) {
  auto *debugger = get_debugger("eval", root, result, channel);
  if (!debugger) {
    return;
  }
  std::string value;
  debugger->eval(root["code"].asString(), value);
  result["content"] = value;
}
DEBUG_COMMAND_HANDLE_IMPL(open_file) {
  auto *debugger = get_debugger("open_file", root, result, channel);
  if (!debugger) {
    return;
  }
  if (!root.isMember("file_name")) {
    result["error"] = "Invalid protocol";
    return;
  }
  auto file_name = root["file_name"].asString();
  std::ifstream ifs;
  const auto &root_path = debugger->get_source_root();
  auto path = util::complete_path(root_path, file_name);
  ifs.open(path);
  if (!ifs) {
    result["error"] = "Cannot open file " + file_name;
    return;
  }
  std::string content;
  int cur_line = 0;
  std::string line_str;
  while (std::getline(ifs, line_str)) {
    cur_line += 1;
    content += std::to_string(cur_line) + "  " + line_str + "\n";
  }
  ifs.close();
  result["file_name"] = file_name;
  result["content"] = content;
}
DEBUG_COMMAND_HANDLE_IMPL(save_file) {
  auto *debugger = get_debugger("save_file", root, result, channel);
  if (!debugger) {
    return;
  }
  if (!root.isMember("file_name") || !root.isMember("content")) {
    result["error"] = "Invalid protocol";
    return;
  }
  auto file_name = root["file_name"].asString();
  auto content = root["content"].asString();
  std::ofstream ofs;
  const auto &root_path = debugger->get_source_root();
  auto path = util::complete_path(root_path, file_name);
  ofs.open(path, std::ios::trunc | std::ios::out);
  if (!ofs) {
    result["error"] = "Cannot open file " + file_name;
    return;
  }
  ofs << content;
  ofs.close();
}
DEBUG_COMMAND_HANDLE_IMPL(add_file) {
  auto *debugger = get_debugger("add_file", root, result, channel);
  if (!debugger) {
    return;
  }
  if (!root.isMember("file_name")) {
    result["error"] = "Invalid protocol";
    return;
  }
  auto file_name = root["file_name"].asString();
  const auto &root_path = debugger->get_source_root();
  auto path = util::complete_path(root_path, file_name);
  std::ofstream ofs;
  ofs.open(path, std::ios::trunc | std::ios::out);
  if (!ofs) {
    result["error"] = "Cannot add file " + file_name;
    return;
  }
  ofs.close();
}
DEBUG_COMMAND_HANDLE_IMPL(delete_file) {
  auto *debugger = get_debugger("delete_file", root, result, channel);
  if (!debugger) {
    return;
  }
  if (!root.isMember("file_name")) {
    result["error"] = "Invalid protocol";
    return;
  }
  auto file_name = root["file_name"].asString();
  const auto &root_path = debugger->get_source_root();
  auto path = util::complete_path(root_path, file_name);
  std::error_code error_code;
  if (!fs::remove(path, error_code)) {
    result["error"] = error_code.message();
  }
}
DEBUG_COMMAND_HANDLE_IMPL(add_dir) {
  auto *debugger = get_debugger("add_dir", root, result, channel);
  if (!debugger) {
    return;
  }
  if (!root.isMember("dir_name")) {
    result["error"] = "Invalid protocol";
    return;
  }
  auto dir_name = root["dir_name"].asString();
  const auto &root_path = debugger->get_source_root();
  auto path = util::complete_path(root_path, dir_name);
  std::error_code error_code;
  if (!fs::create_directory(path, error_code)) {
    result["error"] = error_code.message();
  }
}
DEBUG_COMMAND_HANDLE_IMPL(remove_dir) {
  auto *debugger = get_debugger("remove_dir", root, result, channel);
  if (!debugger) {
    return;
  }
  if (!root.isMember("dir_name")) {
    result["error"] = "Invalid protocol";
    return;
  }
  auto dir_name = root["dir_name"].asString();
  const auto &root_path = debugger->get_source_root();
  auto path = util::complete_path(root_path, dir_name);
  std::error_code error_code;
  if (!fs::remove(path, error_code)) {
    result["error"] = error_code.message();
  }
}
DEBUG_COMMAND_HANDLE_IMPL(rename_dir) {
  auto *debugger = get_debugger("rename_dir", root, result, channel);
  if (!debugger) {
    return;
  }
  if (!root.isMember("old_dir_name") || !root.isMember("new_dir_name")) {
    result["error"] = "Invalid protocol";
    return;
  }
  auto old_dir_name = root["old_dir_name"].asString();
  auto new_dir_name = root["new_dir_name"].asString();
  const auto &root_path = debugger->get_source_root();
  auto old_path = util::complete_path(root_path, old_dir_name);
  auto new_path = util::complete_path(root_path, new_dir_name);
  std::error_code error_code;
  fs::rename(old_path, new_path, error_code);
  if (error_code) {
    result["error"] = error_code.message();
  }
}

DEBUG_COMMAND_HANDLE_IMPL(reload) {
  auto *debugger = get_debugger("reload", root, result, channel);
  if (!debugger) {
    return;
  }
  auto it = debugger_map_.find(debugger->get_name());
  if (it == debugger_map_.end()) {
    result["error"] = "Service not found";
    return;
  }
  // 拷贝名称, reload会重新生成debugger
  auto name = debugger->get_name();
  // 获取对应的服务
  auto *service = it->second.service;
  if (!service->reload()) {
    result["error"] = "Service reload failed";
    return;
  }
  // 重新添加
  service->open_debugger(name);
}

DEBUG_COMMAND_HANDLE_IMPL(get_thread) {
  auto *debugger = get_debugger("get_thread", root, result, channel);
  if (!debugger) {
    return;
  }
  auto it = debugger_map_.find(debugger->get_name());
  if (it == debugger_map_.end()) {
    result["error"] = "Service not found";
    return;
  }
  result["content"] = it->second.service->get_thread_info();
}

DEBUG_COMMAND_HANDLE_IMPL(backtrace) {
  auto *debugger = get_debugger("backtrace", root, result, channel);
  if (!debugger) {
    return;
  }
  std::string content;
  if (!debugger->get_backtrace(-1, content)) {
    result["error"] = "Get backtrace failed";
    return;
  }
  result["content"] = content;
}

DEBUG_COMMAND_HANDLE_IMPL(frame) {
  auto *debugger = get_debugger("frame", root, result, channel);
  if (!debugger) {
    return;
  }
  if (!root.isMember("level")) {
    result["error"] = "Invalid protocol, need attribute level";
    return;
  }
  auto level = root["level"].asInt();
  if (!debugger->frame(level)) {
    result["error"] = "Wrong level";
    return;
  }
  std::string content;
  if (!debugger->get_backtrace(level, content)) {
    result["error"] = "Get backtrace failed";
    return;
  }
  result["content"] = content;
}

DEBUG_COMMAND_HANDLE_IMPL(restart) {
  auto *debugger = get_debugger("restart", root, result, channel);
  if (!debugger) {
    return;
  }
  auto it = debugger_map_.find(debugger->get_name());
  if (it == debugger_map_.end()) {
    result["error"] = "Service not found";
    return;
  }
  // 获取对应的服务
  auto *service = it->second.service;
  // 重启
  if (!service->restart()) {
    result["error"] = "Service reload failed";
    return;
  }
}

DEBUG_COMMAND_HANDLE_IMPL(get_log) {
  auto *debugger = get_debugger("get_log", root, result, channel);
  if (!debugger) {
    return;
  }
  auto it = debugger_map_.find(debugger->get_name());
  if (it == debugger_map_.end()) {
    result["error"] = "Service not found";
    return;
  }
  if (!root.isMember("limit")) {
    result["error"] = "Invalid protocol, need limit";
    return;
  }
  auto limit = std::stoi(root["limit"].asString());
  // 获取对应的服务
  auto *service = it->second.service;
  auto *log_history = service->get_log_history();
  if (log_history) {
    std::stringstream ss;
    log_history->get_history(limit, ss);
    result["content"] = ss.str();
  }
}
